/*
 * Copyright 2011-2012 Redia A/S
 * 
 * This file is part of Redias Library App for Android.
 *
 *  Redia Library App for Android is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  Redia Library App for Android is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License
 *  along with Redia Library App for Android.  If not, see <http://www.gnu.org/licenses/>.
 */
package dk.redia.libraryapp.adapters;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import dk.redia.lib.log.Log;
import dk.redia.lib.net.ImageCache;
import dk.redia.lib.utils.AppApplication;
import dk.redia.lib.utils.AppConfiguration;
import dk.redia.lib.xmlrpc.XmlRpcClientCallTask;
import dk.redia.lib.xmlrpc.XmlRpcClientResultListener;
import dk.redia.lib.xmlrpc.XmlRpcResultDumper;
import dk.redia.lib.xmlrpc.XmlRpcResultUtils;
import dk.redia.libraryapp.R;
import dk.redia.libraryapp.model.Reservation;
import dk.redia.libraryapp.model.SearchResultDataItem;
import dk.redia.libraryapp.net.CoverUrlCache;
import dk.redia.libraryapp.net.CoverUrlCacheListener;
import dk.redia.libraryapp.net.RemoteListManager;
import dk.redia.libraryapp.net.SearchResultsRemoteListManager;
import dk.redia.libraryapp.utils.CategoryNamingUtils;
import dk.redia.libraryapp.utils.CategoryToDefaultCoverMapper;
import dk.redia.libraryapp.xmlrpc.library.XmlRpcClient;

public class SearchResultAdapter extends RemoteListAdapter {

	protected static final String logTag = "SearchResultAdapter";
	
	private Context context;
	private Map<String, String> reservable;
	private Set<XmlRpcClientCallTask> currentSearchExtraTasks; 
	//private DataSetObserverCollection observers;
	//private List<SearchResultDataItem> searchResultDataItems;
	
	public SearchResultAdapter(Context context) {
		super(new SearchResultsRemoteListManager());
		this.context = context;
		this.reservable = new HashMap<String, String>();
		this.currentSearchExtraTasks = new HashSet<XmlRpcClientCallTask>();
	}

	public void search(String query, String contentCategory) {
		Log.d(logTag, "Cancelling " + currentSearchExtraTasks.size() + " pending XML-RPC tasks.");
		for (XmlRpcClientCallTask task : currentSearchExtraTasks) {
			task.cancel(true);
		}
		currentSearchExtraTasks.clear();
		setParam(SearchResultsRemoteListManager.PARAM_KEY_QUERY, query);
		setParam(SearchResultsRemoteListManager.PARAM_KEY_CONTENT_CATEGORY, contentCategory);
		reset();
	}
	
	public View getView(int position, View convertView, ViewGroup parent) {
		View v = convertView;
		if (convertView==null) {
			View existingView = getItemView(position);
			if (existingView!=null) {
				return existingView;
			} else {
				LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
				v = inflater.inflate(R.layout.searchresultitem, null);
			}
		}
		
		int loadState = getLoadState(position);
		
		v = getViewByState(v, loadState);
		updateViewCache(position, null, v);
		
		ensureIndexLoad(position);
		
		if (loadState==RemoteListManager.STATE_LOADED) {
			SearchResultDataItem srdi = (SearchResultDataItem)getItem(position);
			updateViewCache(position, srdi.getIdentifier(), v);
			TextView titleTextView = (TextView)v.findViewById(R.id.searchResultItemTitleTextView);
			TextView creatorTextView = (TextView)v.findViewById(R.id.searchResultItemCreatorTextView);
			TextView descriptionTextView = (TextView)v.findViewById(R.id.searchResultItemDescriptionTextView);
			TextView categoryTextView = (TextView)v.findViewById(R.id.searchResultItemCategoryTextView);
			ImageView coverImageView = (ImageView)v.findViewById(R.id.searchResultItemCoverImageView);
			Button reserveButton = (Button)v.findViewById(R.id.searchResultItemReserveButton);
			ProgressBar reservingProgressBar = (ProgressBar)v.findViewById(R.id.searchResultItemReservingProgressBar);
			String reservationId = reservable.get(srdi.getIdentifier());
			Log.d(logTag, "Reservation id is " + reservationId + " at index " + position+ " for identifier " + srdi.getIdentifier());
			if (reservationId==null) {
				// Probably still checking
				reserveButton.setVisibility(View.INVISIBLE);
				reservingProgressBar.setVisibility(View.VISIBLE);
			} else if (reservationId.equals("")) {
				// We checked, it isn't reservable
				reserveButton.setVisibility(View.INVISIBLE);
				reservingProgressBar.setVisibility(View.GONE);
			} else {
				// We checked, it is indeed reservable
				reserveButton.setVisibility(View.VISIBLE);
				reservingProgressBar.setVisibility(View.GONE);
				Reservation r = new Reservation();
				r.setReservationId(reservationId);
				r.setTitle(srdi.getTitle());
				reserveButton.setTag(r);
			}
			
			categoryTextView.setText(CategoryNamingUtils.getCategoryName(srdi.getType()));
			titleTextView.setText(srdi.getTitle());
			if (srdi.getCreator().length()==0) {
				v.findViewById(R.id.searchResultItemByTextView).setVisibility(View.INVISIBLE);
				creatorTextView.setVisibility(View.INVISIBLE);
			}
			creatorTextView.setText(srdi.getCreator());
			descriptionTextView.setText(
					srdi.getAbstract() + 
					(srdi.getDate().length()>0 ? " (" + srdi.getDate() + ")" : "")
			);
			
			coverImageView.setImageResource(CategoryToDefaultCoverMapper.map(srdi.getType()));
			
			v.setTag(srdi);
			
			if (CoverUrlCache.getInstance().hasCache(srdi.getCoverId())) {
				try {
					ImageCache.getInstance().loadImage(new URL(CoverUrlCache.getInstance().getCoverUrlSync(srdi.getCoverId())), coverImageView, null, 75, 125);
				} catch (MalformedURLException e) {
					e.printStackTrace();
				}
			}
			
			//loadCover(srdi.getIdentifier(), coverImageView);
		} else {
			
		}
		
//		new SearchResultCoverFinder(srdi.getIdentifier(), coverImageView).execute();
		
		return v;
	}

	@Override
	protected View getViewByState(View v, int state) {
		TextView titleTextView = (TextView)v.findViewById(R.id.searchResultItemTitleTextView);
		TextView creatorTextView = (TextView)v.findViewById(R.id.searchResultItemCreatorTextView);
		TextView descriptionTextView = (TextView)v.findViewById(R.id.searchResultItemDescriptionTextView);
		TextView categoryTextView = (TextView)v.findViewById(R.id.searchResultItemCategoryTextView);
		ImageView coverImageView = (ImageView)v.findViewById(R.id.searchResultItemCoverImageView);
		ProgressBar loadingProgressBar = (ProgressBar)v.findViewById(R.id.searchResultItemLoadingProgressBar);
		ProgressBar reservingProgressBar = (ProgressBar)v.findViewById(R.id.searchResultItemReservingProgressBar);
		ImageView arrowRightView = (ImageView)v.findViewById(R.id.searchResultItemArrowRightImageView);
		TextView byTextView = (TextView)v.findViewById(R.id.searchResultItemByTextView);
		
		if (state==RemoteListManager.STATE_LOADED) {
			v.setClickable(true);
			byTextView.setVisibility(View.VISIBLE);
			titleTextView.setVisibility(View.VISIBLE);
			creatorTextView.setVisibility(View.VISIBLE);
			descriptionTextView.setVisibility(View.VISIBLE);
			categoryTextView.setVisibility(View.VISIBLE);
			coverImageView.setVisibility(View.VISIBLE);
			arrowRightView.setVisibility(View.VISIBLE);
			reservingProgressBar.setVisibility(View.VISIBLE);
			loadingProgressBar.setVisibility(View.GONE);
		} else {
			v.setClickable(false);
			titleTextView.setVisibility(View.GONE);
			byTextView.setVisibility(View.GONE);
			creatorTextView.setVisibility(View.GONE);
			descriptionTextView.setVisibility(View.GONE);
			categoryTextView.setVisibility(View.GONE);
			coverImageView.setVisibility(View.GONE);
			arrowRightView.setVisibility(View.GONE);
			reservingProgressBar.setVisibility(View.GONE);
			loadingProgressBar.setVisibility(View.VISIBLE);
		}
		return v;
	}
	
	public void loadedStateChanged(List<Integer> indices, int state) {
		super.loadedStateChanged(indices, state);
		final Map<String, Integer> identifiersToIndex = new HashMap<String, Integer>();
		final Map<String, String> coverIdsToIdentifiers = new HashMap<String, String>();
		for (Integer idx : indices) {
			SearchResultDataItem srdi = (SearchResultDataItem)getItem(idx);
			identifiersToIndex.put(srdi.getIdentifier(), idx);
			coverIdsToIdentifiers.put(srdi.getCoverId(), srdi.getIdentifier());
		}
		String[] identifiersArr = new String[identifiersToIndex.size()];
		identifiersToIndex.keySet().toArray(identifiersArr);
		CoverUrlCache.getInstance().getCoverUrl(coverIdsToIdentifiers.keySet(), new CoverUrlCacheListener() {
			
			public void gotCoverUrls(Map<String, String> coverIdsToUrls) {
				Log.d(logTag, "Received urls for " + coverIdsToUrls.size() + " identifiers");
				for (String coverId : coverIdsToUrls.keySet()) {
					String identifier = coverIdsToIdentifiers.get(coverId);
					View v = getIdentifierView(identifier);
					if (v!=null) {
					ImageView coverImageView = (ImageView)v.findViewById(R.id.searchResultItemCoverImageView);
					try {
						URL u = new URL(coverIdsToUrls.get(coverId));
						Log.d(logTag, "Loading front page from URL " + u);
						ImageCache.getInstance().loadImage(u, coverImageView, null, 75, 125);
					} catch (MalformedURLException e) {
						Log.e(logTag, "Malformed URL when loading cover " + e.getMessage());
						e.printStackTrace();
					}
					}
				}
			}
		});
		// If this is too slow to return, it nukes any previously loaded front pages
		XmlRpcClientCallTask task = XmlRpcClient.getInstance().getObjectExtras(AppConfiguration.getInstance().getCustomerId(), AppConfiguration.getInstance().getApiKey(), identifiersArr, new XmlRpcClientResultListener() {
			
			public void callFailed(String message) {
				Log.e(logTag, "Failed to retrieve object extras: " + message);
				AppApplication.toast(R.string.failedToGetObjectExtrasText, Toast.LENGTH_LONG);
			}
			
			public void callCompleted(Object resultObj) {
				XmlRpcResultDumper.dump(resultObj);
				Map<String, Object> result = XmlRpcResultUtils.unpackStandardResult(resultObj);
				Boolean resultResult = (Boolean)result.get("result");
				if (resultResult) {
					Map<String, Object> resultData = XmlRpcResultUtils.unpackStruct(result.get("data"));
					if (resultData==null) {
						Log.e(logTag, "No data or invalid type in call to getObjectExtras: " +  result.get("message"));
						AppApplication.toast("Error during retrieval of extra object data", Toast.LENGTH_SHORT);
						return;
					}
					// Base iteration on original list, rather than XML-RPC-result, 
					// in case XML-RPC result doesn't contain a result for all 
					// identifiers.
					for (String searchResultIdentifier : identifiersToIndex.keySet()) {
						Boolean isReservable = false;
						String reservationId = "";
						Integer index = identifiersToIndex.get(searchResultIdentifier);
						if (resultData.containsKey(searchResultIdentifier)) {
							Map<String, Object> objectExtrasMap = XmlRpcResultUtils.unpackStruct(resultData.get(searchResultIdentifier));
							Object reservationIdObj = objectExtrasMap.get("reservationId");
							Object isReservableObj = objectExtrasMap.get("isReservable");
							if (reservationIdObj!=null) {
								reservationId = (String)reservationIdObj;
							}
							if (isReservableObj!=null) {
								isReservable = (Boolean)isReservableObj;
							}
						}
						Log.d(logTag, "Noting that search result item with id " + searchResultIdentifier + " at index " + index + " has reservable status " + isReservable);
						if (isReservable) {
							reservable.put(searchResultIdentifier, reservationId);
						} else {
							reservable.put(searchResultIdentifier, "");
						}
						// FIXME This will cause a significant number of views to
						// FIXME get instantiated and stored in the view cache. This is probably a bad idea
						// FIXME We should probably simply invalidate all views currently created
						View v = getView(index, getCachedView(index), null);
						if (v!=null) {
							Log.d(logTag, "Invalidating view at index " + index);
							v.invalidate(); 
						} else {
							Log.d(logTag, "No view currently active for index " + index);
						}
					}
				} else {
					String resultMessage = (String)result.get("message");
					Log.e(logTag, "Failed to retrieve object extras: " + resultMessage);
				}
			}
		});
		currentSearchExtraTasks.add(task);
	}
}
